浅谈内存和指针 ======================= 内存 ******* 计算机内存的地址通常按字节间隔排列。 在计算机中,内存被划分为许多小的存储单元,每个单元通常是一个字节。 每个存储单元都有一个唯一的地址,这些地址通常按顺序连续分配,形成了一个连续的地址空间。 在 ``C`` 语言中,最小的基本数据类型是 ``char`` ,大小为 ``1`` 个字节,即 ``8`` 位。 ``1`` 字节是 ``C`` 语言中内存的最小单元。这是因为计算机内存的寻址通常是以字节为单位,而不是位。 .. code-block:: C void *data = malloc(4 * sizeof(char)); ``malloc`` 函数接受一个整数参数,表示要分配的字节数,返回一个通用指针,指向一个大小为 ``4`` 字节的内存块。 由于内存是在堆上分配的,不像局部变量那样在栈上分配,因此在不再需要时应该释放它,以避免内存泄漏。 .. code-block:: C free(data); 接下来看一些常用的内存操作函数。 #. .. code-block:: C memset(data, 0b11111111, 4 * sizeof(char)); ``memset`` 函数的作用是将 ``data`` 指针指向的内存块的前 ``4`` 个字节的每个字节的所有位都设置为 ``1`` (即 ``0b11111111`` ) 。 #. .. code-block:: C void *new_data = malloc(4 * sizeof(char)); memcpy(new_data, data, 4 * sizeof(char)); memmove(new_data, data, 4 * sizeof(char)); ``memcpy`` 和 ``memmove`` 函数的作用是将指针 ``data`` 所指向的内存块的前4个字节按顺序复制到指针 ``new_data`` 所指向的内存块的前 ``4`` 个字节。 它们的区别在于第一个函数直接进行复制操作,而第二个函数则会先将内容存储到一个临时的内存位置,然后再进行复制操作。 因此,如果第一个函数中 ``data`` 和 ``new_data`` 指向同一块内存,复制的结果是未定义的;而第二个函数则能够正常运行,因为它使用了临时内存来确保复制的安全性。 显然,第一个函数的性能更好,但使用时需要注意指针是否指向相同的内存块。 #. .. code-block:: C int res = memcmp(new_data, data, 4 * sizeof(char)); ``memcmp`` 函数用于按顺序比较 ``data`` 指针指向的内存块和 ``new_data`` 指针指向的内存块的前 ``4`` 个字节。 返回值 ``res`` 的大小代表着比较的结果: #. 如果 ``res`` 小于 ``0`` ,则表示 ``new_data`` 的内容在字典顺序上小于 ``data`` 的内容。 #. 如果 ``res`` 等于 ``0`` ,则表示 ``new_data`` 的内容与 ``data`` 的内容相等。 #. 如果 ``res`` 大于 ``0`` ,则表示 ``new_data`` 的内容在字典顺序上大于 ``data`` 的内容。 指针 ******** 在 ``C`` 语言中,指针是用来存储内存块地址的。 它的值实际上就是一个整数。 #. .. code-block:: C printf("The address of data : %u\n", data); ``The address of data : 2481815280`` 这个地址 ``2481815280`` 表示指针 ``data`` 指向的内存块的起始地址。 简单来说,可以把它想象成内存中的第 ``2481815280`` 个字节位置。 假设指针 ``data`` 指向的内存块大小是 ``4`` 字节,那么从地址 ``2481815280`` 到 ``2481815283`` 的这 ``4`` 个字节都属于这个内存块。 #. .. code-block:: C printf("The address of data : %u\n", data); printf("The address of (char *)data + 1 : %u\n", (char *)data + 1); printf("The address of (short *)data + 1 : %u\n", (short *)data + 1); printf("The address of (int *)data + 1 : %u\n", (int *)data + 1); :: The address of data : 3576725232 The address of (char *)data + 1 : 3576725233 The address of (short *)data + 1 : 3576725234 The address of (int *)data + 1 : 3576725236 通过以上代码我们可以看出,不同类型的指针相邻之间地址的差值并不相同。 ``char*`` 增加 ``1`` 时地址增加 ``1`` , ``short*`` 增加 ``1`` 时地址增加 ``2`` , ``int*`` 增加 ``1`` 时地址增加 ``4`` 。 这反映了不同数据类型所占内存空间大小的差异。 同样,将某个指针加 ``1`` 实际上是得到了当前指针指向内存块紧挨着的下一个内存块的起始地址。 它们的地址差正好是该内存块所占的字节数。 在 ``C`` 语言中,这些指针运算被隐藏在幕后,编译器会自动处理,因此看起来似乎是简单地将指针加 ``1`` ,实际上是移动了一个特定大小的内存块。